using System;
using System.Collections.Generic;
using System.Xml;
using SSP.Api.Interfaces;
using SSP.Api.Options;

namespace SSP.Api
{
    /// <summary>
    /// 
    /// </summary>
    [Serializable]
    public class DirectorAlbum : DirectorWrapper, IDeserializer
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="DirectorAlbum"/> class.
        /// </summary>
        /// <param name="parent">The parent.</param>
        public DirectorAlbum(Director parent) : base(parent)
        {
        }

        /// <summary>
        /// The album's unique ID in the database.
        /// </summary>
        /// <value>The id.</value>
        public int Id { get; set; }

        /// <summary>
        /// The album name.
        /// </summary>
        /// <value>The name.</value>
        public string Name { get; set; }

        /// <summary>
        /// The album description.
        /// </summary>
        /// <value>The description.</value>
        public string Description { get; set; }

        /// <summary>
        /// The full HTTP path to the album's audio file, if set.
        /// </summary>
        /// <value>The audio.</value>
        public string Audio { get; set; }

        /// <summary>
        /// The audio caption.
        /// </summary>
        /// <value>The audio caption.</value>
        public string AudioCaption { get; set; }

        /// <summary>
        /// The date and time the album was last updated.
        /// </summary>
        /// <value>The modified.</value>
        public DateTime Modified { get; set; }

        /// <summary>
        /// The date and time the album was created.
        /// </summary>
        /// <value>The created.</value>
        public DateTime Created { get; set; }

        /// <summary>
        /// If the album is a smart album, this returns 1. Otherwise, returns 0. 
        /// </summary>
        /// <value><c>true</c> if smart; otherwise, <c>false</c>.</value>
        public bool Smart { get; set; }
        
        /// <summary>
        /// The provide place taken, as entered on the album settings screen. 
        /// </summary>
        /// <value>The place taken.</value>
        public string PlaceTaken { get; set; }
        
        /// <summary>
        /// The provide date taken, as entered on the album settings screen. 
        /// </summary>
        /// <value>The date taken.</value>
        public DateTime DateTaken { get; set; }

        /// <summary>
        /// The full HTTP path to the album's preview.
        /// </summary>
        /// <value>The preview.</value>
        public DirectorImage Preview { get; set; }

        /// <summary>
        /// User object containing information on the user who created the album.
        /// </summary>
        /// <value>The creator.</value>
        public DirectorUser Creator { get; set; }

        /// <summary>
        /// User object containing information on the user who last updated the album.
        /// </summary>
        /// <value>The updater.</value>
        public DirectorUser Updater { get; set; }

        /// <summary>
        ///     The album's content. 
        /// </summary>
        /// <value>The contents.</value>
        public DirectorContent[] Contents { get; set; }

        /// <summary>
        /// Returns a single album along with its content. Optional only_active parameter allows you to specify if you want to return active content or all content. only_active defaults to true.
        /// </summary>
        /// <param name="id">The id.</param>
        /// <param name="only_active">if set to <c>true</c> [only_active].</param>
        /// <returns></returns>
        public DirectorAlbum Get(int id, bool only_active)
        {
            var aOpt = new AlbumGetOptions {only_active = only_active};
            return Get(id, aOpt);
        }

        /// <summary>
        /// Returns a single album along with its content. Optional only_active parameter allows you to specify if you want to return active content or all content. only_active defaults to true.
        /// </summary>
        /// <param name="id">The id.</param>
        /// <param name="options">The options.</param>
        /// <returns></returns>
        public DirectorAlbum Get(int id, AlbumGetOptions options)
        {
            Parent.Post.Add("data[only_active]=" + (options.only_active ? 1 : 0));
            Parent.Post.Add("data[album_id]=" + id);
            DirectorResponse response = Parent.send("get_album", "get_album_" + id + "_" + (options.only_active ? 1 : 0));
            return response.Album;
        }

        /// <summary>
        /// Return a list of all albums. Optional only_published parameter allows you to specify if you want to return only published albums or all albums. only_published defaults to true. Optional only_active parameter allows you to specify if you want to return active content or all content. only_active defaults to true.
        /// </summary>
        /// <param name="only_published">if set to <c>true</c> [only_published].</param>
        /// <param name="only_active">if set to <c>true</c> [only_active].</param>
        /// <returns></returns>
        public DirectorAlbum[] All(bool only_published, bool only_active)
        {
            return All(only_published, only_active, false);
        }

        /// <summary>
        /// Return a list of all albums. Optional only_published parameter allows you to specify if you want to return only published albums or all albums. only_published defaults to true. Optional only_active parameter allows you to specify if you want to return active content or all content. only_active defaults to true.
        /// </summary>
        /// <param name="only_published">if set to <c>true</c> [only_published].</param>
        /// <param name="only_active">if set to <c>true</c> [only_active].</param>
        /// <param name="list_only">if set to <c>true</c> [list_only].</param>
        /// <returns></returns>
        public DirectorAlbum[] All(bool only_published, bool only_active, bool list_only)
        {
            var albumOptions = new AlbumAllOptions
                                   {
                                       only_published = only_published,
                                       only_active = only_active,
                                       list_only = list_only
                                   };

            return All(albumOptions);
        }

        /// <summary>
        /// Return a list of all albums. This function accepts an options array that can include any of the following parameters. 
        /// </summary>
        /// <param name="options">The options.</param>
        /// <returns></returns>
        public DirectorAlbum[] All(AlbumAllOptions options)
        {
            Parent.Post.Add("data[only_published]=" + BoolToInt(options.only_published));
            Parent.Post.Add("data[only_active]=" + BoolToInt(options.only_active));
            Parent.Post.Add("data[list_only]=" + BoolToInt(options.list_only));
            Parent.Post.Add("data[only_smart]=" + BoolToInt(options.only_smart));
            Parent.Post.Add("data[exclude_smart]=" + BoolToInt(options.exclude_smart));
            DirectorResponse response = Parent.send("get_album_list", "get_album_list_" + (options.only_published ? 1 : 0) + "_" + (options.only_active ? 1 : 0) + "_" + (options.list_only ? 1 : 0) + "_" + (options.only_smart ? 1 : 0) + "_" + (options.exclude_smart ? 1 : 0));
            return response.Albums;
        }

        /// <summary>
        /// Retrieves the galleries that the provided album is a part of. Only gallery data is returned, no associated albums or their content are returned.
        /// </summary>
        /// <param name="album_id">The album_id.</param>
        /// <param name="exclude">The exclude.</param>
        /// <returns></returns>
        public DirectorGallery[] Galleries(int album_id, int exclude)
        {
            var aOpt = new AlbumGalleriesOptions();
            if( ! aOpt.exclude.Contains(exclude.ToString()) )
            {
                aOpt.exclude.Add(exclude.ToString());
            }
            return Galleries(album_id, aOpt);
        }

        /// <summary>
        /// Retrieves the galleries that the provided album is a part of. Only gallery data is returned, no associated albums or their content are returned.
        /// </summary>
        /// <param name="album_id">The album_id.</param>
        /// <param name="options">The options.</param>
        /// <returns></returns>
        public DirectorGallery[] Galleries(int album_id, AlbumGalleriesOptions options)
        {
            Parent.Post.Add("data[album_id]=" + album_id);
            Parent.Post.Add("data[exclude]=" + options.ToQueryString());
            DirectorResponse response = Parent.send("get_associated_galleries", "get_associated_galleries_" + album_id + "_" + options.ToQueryString());
            return response.Galleries;
        }

        /// <summary>
        /// Returns a single album along with its content. Optional only_active parameter allows you to specify if you want to return active content or all content. only_active defaults to true.
        /// </summary>
        /// <param name="id">The id.</param>
        /// <returns></returns>
        public DirectorAlbum Get(int id)
        {
            return Get(id, true);
        }

        #region Implementation of IDeserializer

        /// <summary>
        /// Gets or sets the <see cref="System.String"/> with the specified property name.
        /// </summary>
        /// <value></value>
        public string this[string propertyName]
        {
            get { return _sDict[propertyName]; }
            set { _sDict[propertyName] = value; }
        }

        private readonly Dictionary<string, string> _sDict = new Dictionary<string, string>();

        /// <summary>
        /// Deserializes from an XmlReader and sets the object's properties.
        /// </summary>
        /// <param name="reader">The reader.</param>
        public void DeserializeFromReader(XmlReader reader)
        {
            // Start to use the reader.
            reader.Read();

            // Read all elements
            while (reader.NodeType != XmlNodeType.EndElement)
            {
                switch (reader.Name)
                {
                    case "id":
                        Id = reader.ReadElementContentAsInt();
                        break;
                    case "name":
                        Name = reader.ReadElementContentAsString();
                        break;
                    case "description":
                        Description = reader.ReadElementContentAsString();
                        break;
                    case "audio":
                        Audio = reader.ReadElementContentAsString();
                        break;
                    case "audio_caption":
                        AudioCaption = reader.ReadElementContentAsString();
                        break;
                    case "modified":
                        Modified = DirectorUtils.ConvertFromUnixTimestamp(reader.ReadElementContentAsString());
                        break;
                    case "created":
                        Created = DirectorUtils.ConvertFromUnixTimestamp(reader.ReadElementContentAsString());
                        break;
                    case "preview":
                        Preview = new DirectorImage(Parent);
                        Preview.DeserializeFromReader(reader);
                        break;
                    case "creator":
                        Creator = new DirectorUser(Parent);
                        Creator.DeserializeFromReader(reader);
                        break;
                    case "updater":
                        Updater = new DirectorUser(Parent);
                        Updater.DeserializeFromReader(reader);
                        break;
                    case "contents":
                        Contents = getContents(reader);
                        break;
                    case "place_taken":
                        PlaceTaken = reader.ReadElementContentAsString();
                        break;
                    case "date_taken":
                        DateTaken = DirectorUtils.ConvertFromUnixTimestamp(reader.ReadElementContentAsString());
                        break;
                    case "smart":
                        Smart = DirectorUtils.ToBoolean(reader.ReadElementContentAsString());
                        break;
                    default:
                        System.Diagnostics.Trace.WriteLine("Unhandled xml element name '" + reader.Name + "'.");
                        _sDict[reader.Name] = reader.ReadElementContentAsString();
                        break;
                }
                reader.MoveToContent();
            }

            // Extremely important to read the node to its end.
            // next call of the reader methods will crash if not called.
            reader.ReadEndElement();
        }

        #endregion
    }
}